import { KEYBOARD_KEY, MathTool } from '@amcax/base';
import Base from '@/common/cmdBase/cmdBase';
import { labelManager, lineManager, LineType } from '@/common/preview';
import { EntityInfo, EntityPartType, THREE } from '@amcax/web-render';
import { ManagerId } from 'nextcae-base';
import { Vector3Tuple } from 'three';
import { Dispatch, SetStateAction } from 'react';
import {
  createAngleArc,
  getLineLen,
  getLinePlaneAngles,
  getPointByRatio,
  getPointByVecLen,
} from '@/common/utils';

export enum MeasureMode {
  NONE = 'NONE', // 无
  POSITION = 'POSITION', // 位置
  DIS = 'DIS', // 距离
  LEN = 'LEN', // 长度
  LEN_SPLIT = 'LEN_SPLIT', // 长度 分开
  ANGLE = 'ANGLE', // 角度
  AREA = 'AREA', // 面积
  VOLUME = 'VOLUME', // 体积
  ANGLEXY = 'XY', // 和XY轴角度
  ANGLEYZ = 'YZ', // 和YZ轴角度
  ANGLEZX = 'ZX', // 和ZX轴角度
}
type AxisType = MeasureMode.ANGLEXY | MeasureMode.ANGLEYZ | MeasureMode.ANGLEZX;
export default class CmdInteractor extends Base {
  arcPlane: THREE.Mesh = null;
  pickPointMap: Map<string, Vector3Tuple> = new Map();
  axisMesh: THREE.Mesh = null;
  private measureMode: MeasureMode = null;
  private linePointMap: Map<string, Vector3Tuple[]> = new Map();
  // private
  setMeasureModel(mode: MeasureMode) {
    this.measureMode = mode;
    this.setMeasureInfo();
  }
  get pickPointArr(): Vector3Tuple[] {
    return Array.from(this.pickPointMap.values());
  }
  get linePointArr(): Vector3Tuple[][] {
    return Array.from(this.linePointMap.values());
  }
  constructor(
    private readonly setShowResult: Dispatch<SetStateAction<number>>,
    public exitFunc: () => void,
  ) {
    super();
    this.registerEvents();
    this.init();
  }
  init() {
    this.clear();
    this.release();
    this.pickPointMap.clear();
    this.linePointMap.clear();
    this.axisMesh = null;
    lineManager().createUpdateLine();
    // 激活拾取
    this.interactionMgr.enablePick = true;
  }
  isEntType(type: EntityPartType) {
    if (Array.isArray(this.pickType)) {
      return this.pickType.includes(type);
    } else {
      return this.pickType === type;
    }
  }
  idMapKey(e: EntityInfo) {
    return e.dxid + '-' + e.type + '-' + e.idx;
  }
  onMouseLeft = async (e, selected: boolean) => {
    if (selected) {
      if (this.isEntType(EntityPartType.point)) {
        this.pickPointMap.set(this.idMapKey(e), e.point);
      } else {
        const temp = [];
        const points = this.mainRenderer
          .getEntityManager(ManagerId.Mesh)
          .getEntityData(e.dxid, e.type, e.idx);
        for (let i = 0; i < points.length; i += 3) {
          temp.push([points[i], points[i + 1], points[i + 2]]);
        }
        this.linePointMap.set(this.idMapKey(e), temp);
      }
    } else {
      if (this.isEntType(EntityPartType.point)) {
        this.pickPointMap.delete(this.idMapKey(e));
      } else {
        this.linePointMap.delete(this.idMapKey(e));
      }
    }
    const len = this.pickPointMap.size;
    this.measureMode =
      len === 1
        ? MeasureMode.POSITION
        : len === 2
          ? MeasureMode.DIS
          : len === 3
            ? MeasureMode.ANGLE
            : MeasureMode.LEN;
    if (this.isEntType(EntityPartType.line)) {
      this.measureMode = MeasureMode.LEN;
    }
    lineManager().clearLine();
    this.setMeasureInfo();
    this.setShowResult(this.pickPointMap.size || this.linePointMap.size);
  };
  setMeasureInfo = () => {
    this.removeArcPlane();
    labelManager().removeCss2DLabel();
    switch (this.measureMode) {
      case MeasureMode.POSITION: {
        this.setPosition(this.pickPointArr[0]);
        labelManager().setCss2dOffset();
        break;
      }
      case MeasureMode.DIS: {
        this.setDisInfo();
        labelManager().setCss2dOffset();
        lineManager().clearLine(LineType.ARC);
        lineManager().createUpdateLine(this.pickPointArr);
        break;
      }
      case MeasureMode.ANGLE: {
        this.setAngleInfo();
        labelManager().setCss2dOffset({ x: 0, y: 0 });
        break;
      }
      case MeasureMode.LEN: {
        this.setLenInfo();
        labelManager().setCss2dOffset({ x: 0, y: 0 });
        lineManager().clearLine(LineType.ARC);
        if (this.isEntType(EntityPartType.point)) {
          lineManager().createUpdateLine(Array.from(this.pickPointArr));
        }
        break;
      }
      case MeasureMode.LEN_SPLIT: {
        labelManager().removeCss2DLabel();
        this.setLenInfo('L', true);
        lineManager().clearLine(LineType.ARC);
        break;
      }
      case MeasureMode.ANGLEXY:
      case MeasureMode.ANGLEYZ:
      case MeasureMode.ANGLEZX: {
        this.setAngleByAxis(this.measureMode);
        labelManager().setCss2dOffset({ x: 0, y: 0 });
        break;
      }
      default: {
        break;
      }
    }
    this.mainRenderer.render();
  };
  // 设置位置信息
  setPosition = (v3Arr: Vector3Tuple) => {
    const div = document.createElement('div');
    div.innerHTML = `
        <div>X: ${v3Arr[0].toFixed(2)}</div>
        <div>Y: ${v3Arr[1].toFixed(2)}</div>
        <div>Z: ${v3Arr[2].toFixed(2)}</div>
      `;
    labelManager().setCss2dLabel(div, new THREE.Vector3(...v3Arr));
  };
  setDisInfo = () => {
    if (this.pickPointMap.size === 2) {
      this.setLenInfo('D');
    }
  };
  setAngleInfo = () => {
    const v3Arr: THREE.Vector3[] = this.pointChangeV3();
    const line1Start = v3Arr[1]; // 线段1的起点
    const line1End = v3Arr[0]; // 线段1的终点
    const line2Start = v3Arr[v3Arr.length - 2]; // 线段2的起点
    const line2End = v3Arr[v3Arr.length - 1]; // 线段2的终点
    // 计算线段的方向向量
    this.setAngle([line1Start, line1End, line2Start, line2End]);
  };
  setAngleByAxis = (axis: AxisType) => {
    const v3Arr: THREE.Vector3[] = this.pointChangeV3();
    const start = v3Arr[0]; // 线段1的起点
    const end = v3Arr[1]; // 线段1的终点
    // 投影到 XY 平面（Z=0）
    const xyProjectionEnd = (() => {
      const temp = start.clone();
      if (axis === MeasureMode.ANGLEXY) {
        return temp.setZ(end.z);
      } else if (axis === MeasureMode.ANGLEYZ) {
        return temp.setX(end.x);
      } else {
        return temp.setY(end.y);
      }
    })(); // 线段1在 XY 平面的投影终点
    lineManager().createUpdateLine(
      [end, xyProjectionEnd].map((item) => item.toArray()),
      { dashed: true },
      LineType.A_LINE,
    );
    const res = getLinePlaneAngles(start, end, axis);
    // this.setCss2dLabel(`A: ${res[axis].toFixed(2)}`, end);
    this.setArcPlane([end, start, end, xyProjectionEnd], res[axis]);
    // this.setPlane(axis, end);
  };
  /**
   * 计算线段长度
   * @prefix  显示的前缀
   * @isSplit 是否分开显示
   * */
  setLenInfo = (prefix = 'L', isSplit: boolean = false) => {
    if (this.isEntType(EntityPartType.point)) {
      const v3Arr: THREE.Vector3[] = this.pointChangeV3();
      if (v3Arr.length > 0) {
        const { totalLength } = getLineLen(v3Arr); // 总长度和当前长度
        const [center] = getPointByRatio(v3Arr, 0.5, 'ratio');
        labelManager().setCss2dLabel(
          `${prefix}: ${totalLength.toFixed(2)}`,
          center,
        );
      }
    } else if (this.isEntType(EntityPartType.line)) {
      const v3Arr: THREE.Vector3[][] = this.lineChangeV3();
      if (v3Arr.length) {
        let totalLen = 0;
        const centers = [];
        const lenArr = [];
        v3Arr.forEach((item) => {
          const { totalLength } = getLineLen(item); // 总长度和当前长度
          totalLen += totalLength;
          const [center] = getPointByRatio(item, 0.5, 'ratio');
          centers.push(center);
          lenArr.push(totalLength);
        });
        if (isSplit) {
          lenArr.forEach((item, index) => {
            labelManager().addExtraCss2DLabel(
              labelManager().createLabel(
                `${prefix}: ${item.toFixed(2)}`,
                centers[index],
              ),
            );
          });
        } else {
          if (centers.length === 1) {
            labelManager().setCss2dLabel(
              `${prefix}: ${totalLen.toFixed(2)}`,
              centers[0],
            );
            return;
          }
          const [center] = getPointByRatio(centers, 0.5, 'ratio');
          labelManager().setCss2dLabel(
            `${prefix}: ${totalLen.toFixed(2)}`,
            center,
          );
        }
      }
    }
  };
  setAngle(points: THREE.Vector3[]) {
    const [line1Start, line1End, line2Start, line2End] = points;
    const direction1 = new THREE.Vector3().subVectors(line1End, line1Start);
    const direction2 = new THREE.Vector3().subVectors(line2End, line2Start);

    // 计算两向量之间的弧度角（0 ~ π）
    const angleRadians = direction1.angleTo(direction2);
    const deg = MathTool.radToDeg(angleRadians); // 转换为角度;
    lineManager().createUpdateLine(points.map((v) => v.toArray()));
    this.setArcPlane([line1Start, line1End, line2Start, line2End], deg);
  }
  setArcPlane([line1Start, line1End, line2Start, line2End], deg = 0) {
    deg = MathTool.fixNumber(deg, 2); // 转换为角度;
    // 创建夹角圆弧
    const res = createAngleArc(
      { start: line1Start, end: line1End },
      { start: line2Start, end: line2End },
      { radius: this.getLenByZoom() },
    );
    // 创建圆弧线对象
    lineManager().createUpdateLine(res.arcPoints, null, LineType.ARC);
    const labelP = new THREE.Vector3(
      ...res.arcPoints[Math.ceil((res.arcPoints.length - 1) / 2)],
    );
    const vec = new THREE.Vector3().subVectors(labelP, line2Start).normalize();
    labelManager().setCss2dLabel(
      `A: ${deg}`,
      getPointByVecLen(vec, this.getLenByZoom(20), labelP),
    );
    this.arcPlane = res.mesh;
    this.assisMgr.group.add(res.mesh);
  }
  removeArcPlane() {
    this.assisMgr.group.remove(this.arcPlane);
    this.arcPlane = null;
  }
  pointChangeV3 = () => {
    if (this.isEntType(EntityPartType.point)) {
      return this.pickPointArr.map((item) => new THREE.Vector3(...item));
    }
    return [];
  };
  lineChangeV3 = () => {
    if (this.isEntType(EntityPartType.line)) {
      return this.linePointArr.map((item) => {
        return item.map((v) => new THREE.Vector3(...v));
      });
    }
    return [];
  };
  lineChange;
  onKeyUp = (e) => {
    if (e.key === KEYBOARD_KEY.ESC) {
      if (this.pickPointMap.size === 0 && this.linePointMap.size === 0) {
        this.exitFunc?.();
      }
      this.destroy();
    }
  };

  destroy = (close = false) => {
    this.clear();
    this.setShowResult(0);
    this.release();
    if ((this.pickPointMap.size > 0 || this.linePointMap.size > 0) && !close) {
      this.pickPointMap.clear();
      this.linePointMap.clear();
      lineManager().createUpdateLine();
    } else {
      this.unregisterEvents();
    }
  };
  release() {
    lineManager().clearLine();
    // 关闭拾取
    this.interactionMgr.enablePick = false;
  }
}
